package com.tally.server.service;

import com.tally.server.domain.Cost;
import com.tally.server.domain.CostExample;
import com.tally.server.dto.CostBatchDTO;
import com.tally.server.dto.CostDTO;
import com.tally.server.dto.EverydayCostDTO;
import com.tally.server.enums.CostTypeEnum;
import com.tally.server.mapper.CostMapper;
import com.tally.server.utils.CopyUtil;
import com.tally.server.utils.DateUtil;
import com.tally.server.utils.UUIDUtil;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;

@Service
public class CostService {
    private static final Logger logger = LoggerFactory.getLogger(CostService.class);

    @Autowired
    private CostMapper costMapper;

    /**
     * 保存，id有值时更新，无值时新增
     * @return 新增返回1，修改返回2
     */
    public int save(CostDTO costDTO) {
        Cost cost = CopyUtil.copy(costDTO, Cost.class);
        if (StringUtils.isEmpty(costDTO.getId())) {
            this.insert(cost);
            return 1;
        } else {
            this.update(cost);
            return 2;
        }
    }

    public void saveBatch(CostBatchDTO costBatchDTO) {
        Date recordStartDate = costBatchDTO.getRecordStartDate();
        Date recordEndDate = costBatchDTO.getRecordEndDate();
        List<Date> dateList = DateUtil.dateList(recordStartDate, recordEndDate);
        for (Date date : dateList) {
            Cost cost = CopyUtil.copy(costBatchDTO, Cost.class);
            cost.setRecordDate(date);
            insert(cost);
        }
    }

    /**
     * 新增
     */
    private void insert(Cost cost) {
        Date now = new Date();
        cost.setCreateTime(now);
        cost.setUpdateTime(now);
        cost.setId(UUIDUtil.getShortUUID());
        costMapper.insert(cost);
    }

    /**
     * 更新
     */
    private void update(Cost cost) {
        cost.setUpdateTime(new Date());
        costMapper.updateByPrimaryKey(cost);
    }

    /**
     * 删除
     */
    public void delete(String id) {
        costMapper.deleteByPrimaryKey(id);
    }

    /**
     * 通过id查询
     * @param id
     * @return
     */
    public CostDTO selectById(String id){
        Cost cost = costMapper.selectByPrimaryKey(id);
        return CopyUtil.copy(cost,CostDTO.class);
    }

    /**
     * 根据年月查询该月支出列表
     * @param yearI
     * @param monthI
     * @return
     */
    public List<CostDTO> getMonthCost(Integer yearI, Integer monthI, String userId, boolean desc) {

        Date startDate = getStartDate(yearI,monthI);
        Date endDate = getEndDate(yearI,monthI);

        CostExample costExample = new CostExample();
        costExample.createCriteria()
                .andUserIdEqualTo(Integer.parseInt(userId))
                .andRecordDateBetween(startDate,endDate);
        if(desc){
            costExample.setOrderByClause("record_date desc,cost_type");
        }else{
            costExample.setOrderByClause("record_date asc,cost_type");
        }

        List<Cost> costList = costMapper.selectByExample(costExample);
        return CopyUtil.copyList(costList,CostDTO.class);
    }

    /**
     * 得到某年的支出列表
     * @param yearI
     * @param userId
     * @return
     */
    public List<CostDTO> getYearCost(Integer yearI, String userId, boolean asc) {
        Date startDate = getStartDate(yearI,1);
        Date endDate = getEndDate(yearI,12);

        CostExample costExample = new CostExample();
        costExample.createCriteria()
                .andUserIdEqualTo(Integer.parseInt(userId))
                .andRecordDateBetween(startDate,endDate);
        if(asc){
            costExample.setOrderByClause("record_date");
        }
        List<Cost> costList = costMapper.selectByExample(costExample);
        return CopyUtil.copyList(costList,CostDTO.class);
    }

    /**
     * 得到某年某月的第一天
     * @param yearI
     * @param monthI
     * @return
     */
    private Date getStartDate(Integer yearI,Integer monthI){
        LocalDate startLocalDate = LocalDate.of(yearI,monthI,1);
        return DateUtil.localDate2Date(startLocalDate);
    }

    /**
     * 得到某年某月的最后一天
     * @param yearI
     * @param monthI
     * @return
     */
    private Date getEndDate(Integer yearI,Integer monthI){
        LocalDate endLocalDate = LocalDate.of(yearI,monthI,DateUtil.lastDayOf(yearI,monthI));
        return DateUtil.localDate2Date(endLocalDate);
    }

    /**
     * 得到某年的支出总和
     * @param yearI
     * @param userId
     * @return
     */
    public BigDecimal getYearSum(Integer yearI, String userId) {
        logger.info("{}年支出求和",yearI);
        List<CostDTO> costDTOList = getYearCost(yearI, userId, false);
        BigDecimal sum = costDTOList.stream().map(CostDTO::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        return sum;
    }

    /**
     * 得到某年某月的支出总和
     * @param yearI
     * @param monthI
     * @param userId
     * @return
     */
    public BigDecimal getMonthSum(Integer yearI, Integer monthI, String userId) {

        logger.info("{}年{}月支出求和",yearI,monthI);
        List<CostDTO> costDTOList = getMonthCost(yearI, monthI, userId, true);
        BigDecimal sum = costDTOList.stream().map(CostDTO::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        return sum;
    }

    /**
     * 得到某月中每天的支出总额，空补0
     * @param yearI
     * @param monthI
     * @param userId
     * @return
     */
    public List<BigDecimal> everydayCostSum(int yearI, int monthI, String userId) {
        List<CostDTO> monthCostList = getMonthCost(yearI, monthI, userId, false);
        List<BigDecimal> list = new ArrayList<>();
        int days = DateUtil.lastDayOf(yearI, monthI);
        Date firstDay = DateUtil.localDate2Date(LocalDate.of(yearI,monthI,1));
        boolean exit = false;
        for(int i=0;i<monthCostList.size();){
            CostDTO costDTO = monthCostList.get(i);
            if(!costDTO.getRecordDate().equals(firstDay)){
                list.add(BigDecimal.ZERO);
            }else{
                BigDecimal sum = BigDecimal.ZERO;
                do{
                    sum = sum.add(costDTO.getMoney());
                    if(i+1 == monthCostList.size()){
                        exit = true;
                        break;
                    }
                    costDTO = monthCostList.get(++i);
                }while (costDTO.getRecordDate().equals(firstDay));
                list.add(sum);
            }
            firstDay = plusOneDay(firstDay);
            if(exit){
                break;
            }
        }
        while(list.size()<days){
            list.add(BigDecimal.ZERO);
        }
        return list;
    }

    /**
     * 日期增加一天
     * @param date
     * @return
     */
    private Date plusOneDay(Date date){
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, 1);
        return calendar.getTime();
    }

    /**
     * 得到某年某月各项支出之和
     * @param yearI
     * @param monthI
     * @param userId
     * @return
     */
    public Map<String, BigDecimal> eachCostSum(int yearI, int monthI, String userId) {
        logger.info("计算{}年{}月各项支出总和",yearI,monthI);
        List<CostDTO> costDTOList = getMonthCost(yearI, monthI, userId, false);
        return getCostSumMap(costDTOList);
    }

    /**
     * 得到某年各项支出之和
     * @param yearI
     * @param userId
     * @return
     */
    public Map<String,BigDecimal> eachCostSum(int yearI,String userId){
        logger.info("计算{}年各项支出总和",yearI);
        List<CostDTO> costDTOList = getYearCost(yearI, userId, false);
        return getCostSumMap(costDTOList);
    }

    /**
     * 提取支出列表中的各项支出
     * @param costDTOList
     * @return 包含各项支出之和的map
     */
    private Map<String, BigDecimal> getCostSumMap(List<CostDTO> costDTOList) {
        Map<String,BigDecimal> map = new HashMap<>();
        for(CostTypeEnum costTypeEnum: CostTypeEnum.values()){
            map.put(costTypeEnum.getCode(),BigDecimal.ZERO);
        }
        for(CostDTO costDTO: costDTOList){
            String type = costDTO.getCostType();
            BigDecimal money = map.get(type);
            map.put(type,money.add(costDTO.getMoney()));
        }
        return map;
    }

    /**
     * 得到每月支出总和，空补0
     * @param yearI
     * @param userId
     * @return list
     */
    public List<BigDecimal> monthCostSum(int yearI, String userId) {
        logger.info("计算{}年各月总支出",yearI);
        List<BigDecimal> decimalList = new ArrayList<>();
        List<CostDTO> costDTOList = getYearCost(yearI, userId, true);
        boolean exit = false;
        for(int i=0;i<costDTOList.size();){
            CostDTO costDTO = costDTOList.get(i);
            int curMonth;
            if((curMonth = DateUtil.getCalendar(costDTO.getRecordDate()).get(Calendar.MONTH)) == decimalList.size()){
                BigDecimal sum = BigDecimal.ZERO;
                do{
                    sum = sum.add(costDTO.getMoney());
                    if(i+1 == costDTOList.size()){
                        exit = true;
                        break;
                    }
                }while (curMonth == DateUtil.getCalendar((costDTO = costDTOList.get(++i)).getRecordDate()).get(Calendar.MONTH));
                decimalList.add(sum);
            }else{
                int size = decimalList.size();
                for(int j=0;j<curMonth-size;j++){
                    decimalList.add(BigDecimal.ZERO);
                }
            }
            if(exit){
                break;
            }
        }
        while(decimalList.size()<12){
            decimalList.add(BigDecimal.ZERO);
        }
        return decimalList;
    }

    public List<EverydayCostDTO> getEverydayList(Date startDate, Date endDate, Integer userId) {
        CostExample costExample = new CostExample();
        costExample.createCriteria()
                .andUserIdEqualTo(userId)
                .andRecordDateBetween(startDate, endDate);
        costExample.setOrderByClause("record_date asc");
        List<Cost> costList = costMapper.selectByExample(costExample);

        if (CollectionUtils.isEmpty(costList)) {
            return null;
        }

        boolean exit = false;
        List<EverydayCostDTO> resultList = new ArrayList<>();
        Date currentDate = startDate;
        // 组装数据
        for (int i=0;i<costList.size();) {
            Cost cost = costList.get(i);
            EverydayCostDTO everydayCostDTO = new EverydayCostDTO(currentDate);
            if (cost.getRecordDate().equals(currentDate)) {
                // 如果日期相同则继续循环
                do {
                    // 从map里拿到对应type下的money然后求和
                    String typeCode = cost.getCostType();
                    String typeDesc = CostTypeEnum.getDesc(typeCode);
                    BigDecimal money = everydayCostDTO.getTypeMoneyMap().get(typeDesc);
                    everydayCostDTO.getTypeMoneyMap().put(typeDesc, money.add(cost.getMoney()));
                    everydayCostDTO.setSum(everydayCostDTO.getSum().add(cost.getMoney()));

                    if (i + 1 == costList.size()) {
                        exit = true;
                        break;
                    }
                    cost = costList.get(++i);
                } while (cost.getRecordDate().equals(currentDate));
            }
            resultList.add(everydayCostDTO);
            if (exit) {
                break;
            }
            currentDate = DateUtils.addDays(currentDate, 1);
        }
        // 如果还未到endDate，把后面全补0
        while (currentDate.before(endDate)) {
            currentDate = DateUtils.addDays(currentDate, 1);
            EverydayCostDTO everydayCostDTO = new EverydayCostDTO(currentDate);
            resultList.add(everydayCostDTO);
        }
        return resultList;
    }
}
